package com.kexio.file.service.impl;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kexio.file.entity.ShareLink;
import com.kexio.file.mapper.ShareLinkMapper;
import com.kexio.file.service.FileInfoService;
import com.kexio.file.service.ShareLinkService;
import com.kexio.file.vo.ShareLinkVO;
import com.kexio.file.config.FileStorageProperties;
import cn.hutool.json.JSONUtil;
import cn.hutool.json.JSONObject;
import java.security.MessageDigest;
import java.security.SecureRandom;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.digest.BCrypt;

/**
 * 文件分享链接服务实现
 * 
 * @author Kexio Team
 * @since 1.0.0
 */
@Service
public class ShareLinkServiceImpl extends ServiceImpl<ShareLinkMapper, ShareLink> implements ShareLinkService {

    private static final Logger log = LoggerFactory.getLogger(ShareLinkServiceImpl.class);

    @Autowired
    private FileInfoService fileInfoService;
    @Autowired
    private FileStorageProperties storageProperties;

    @Override
    @Transactional
    public ShareLinkVO createShareLink(String fileId, Integer shareType, String password,
                                       LocalDateTime validUntil, Integer maxAccessCount, 
                                       Integer maxDownloadCount, String description) {
        if (!StringUtils.hasText(fileId) || shareType == null) {
            throw new IllegalArgumentException("文件ID和分享类型不能为空");
        }

        // 验证文件是否存在
        if (fileInfoService.getById(fileId) == null) {
            throw new IllegalArgumentException("文件不存在");
        }

        try {
            // 1. 生成分享码
            String shareCode = generateShareCode();

            // 2. 创建分享链接记录
            ShareLink shareLink = new ShareLink();
            shareLink.setFileId(fileId);
            shareLink.setShareCode(shareCode);
            shareLink.setShareType(shareType);
            // 密码处理：优先使用 BCrypt
            if (StringUtils.hasText(password)) {
                String hashed = BCrypt.hashpw(password, BCrypt.gensalt());
                shareLink.setSharePassword(hashed);
                // 清理旧盐
                shareLink.setExtraAttributes(null);
            }
            shareLink.setAccessPermission(1); // 默认可访问
            shareLink.setMaxAccessCount(maxAccessCount);
            shareLink.setCurrentAccessCount(0);
            shareLink.setMaxDownloadCount(maxDownloadCount);
            shareLink.setCurrentDownloadCount(0);
            shareLink.setValidFrom(LocalDateTime.now());
            shareLink.setValidUntil(validUntil);
            shareLink.setDescription(description);
            shareLink.setStatus(1); // 启用状态
            // 注意：不需要手动设置createTime和createBy，BaseFieldsMetaObjectHandler会自动填充
            // shareLink.setCreateTime(LocalDateTime.now());
            // shareLink.setCreateBy(getCurrentUserId());

            // 3. 保存到数据库 - 系统会自动填充审计字段和租户ID
            save(shareLink);

            log.info("创建分享链接成功: fileId={}, shareId={}, shareCode={}, shareType={}", 
                    fileId, shareLink.getId(), shareCode, shareType);

            return convertToVO(shareLink);

        } catch (Exception e) {
            log.error("创建分享链接失败: fileId={}, shareType={}", fileId, shareType, e);
            throw new RuntimeException("创建分享链接失败: " + e.getMessage());
        }
    }

    @Override
    public ShareLinkVO getByShareCode(String shareCode) {
        if (!StringUtils.hasText(shareCode)) {
            throw new IllegalArgumentException("分享码不能为空");
        }

        ShareLink shareLink = baseMapper.selectByShareCode(shareCode);
        return shareLink != null ? convertToVO(shareLink) : null;
    }

    @Override
    public ShareLinkVO validateShareAccess(String shareCode, String password, String clientIp) {
        if (!StringUtils.hasText(shareCode)) {
            throw new IllegalArgumentException("分享码不能为空");
        }

        try {
            ShareLink shareLink = baseMapper.selectByShareCode(shareCode);
            if (shareLink == null) {
                throw new IllegalArgumentException("分享链接不存在");
            }

            // 检查分享链接状态
            if (shareLink.getStatus() != 1) {
                throw new IllegalStateException("分享链接已禁用");
            }

            LocalDateTime now = LocalDateTime.now();

            // 检查有效期
            if (shareLink.getValidUntil() != null && now.isAfter(shareLink.getValidUntil())) {
                throw new IllegalStateException("分享链接已过期");
            }

            // 检查访问次数限制
            if (shareLink.getMaxAccessCount() != null && 
                shareLink.getCurrentAccessCount() >= shareLink.getMaxAccessCount()) {
                throw new IllegalStateException("分享链接访问次数已达上限");
            }

            // 检查密码（如果需要）
            if (shareLink.getShareType() == 2 && StringUtils.hasText(shareLink.getSharePassword())) {
                if (!StringUtils.hasText(password)) {
                    throw new IllegalStateException("分享密码错误");
                }
                String stored = shareLink.getSharePassword();
                boolean pass = false;
                // 优先 BCrypt 验证
                if (stored.startsWith("$2")) {
                    pass = BCrypt.checkpw(password, stored);
                } else {
                    // 兼容带盐SHA-256或明文
                    String salt = extractSalt(shareLink.getExtraAttributes());
                    if (StringUtils.hasText(salt)) {
                        pass = verifyPassword(password, salt, stored);
                    } else {
                        pass = password.equals(stored);
                    }
                }
                if (!pass) {
                    throw new IllegalStateException("分享密码错误");
                }
            }

            log.info("分享链接验证成功: shareCode={}, clientIp={}", shareCode, clientIp);
            return convertToVO(shareLink);

        } catch (Exception e) {
            log.warn("分享链接验证失败: shareCode={}, clientIp={}, error={}", shareCode, clientIp, e.getMessage());
            throw e;
        }
    }

    @Override
    @Transactional
    public boolean recordAccess(String shareCode, String clientIp) {
        if (!StringUtils.hasText(shareCode)) {
            return false;
        }

        try {
            ShareLink shareLink = baseMapper.selectByShareCode(shareCode);
            if (shareLink == null) {
                return false;
            }

            // 更新访问统计
            int affectedRows = baseMapper.updateAccessStats(shareLink.getId(), LocalDateTime.now(), clientIp);
            
            log.info("记录分享访问成功: shareCode={}, clientIp={}, affectedRows={}", shareCode, clientIp, affectedRows);
            return affectedRows > 0;

        } catch (Exception e) {
            log.error("记录分享访问失败: shareCode={}, clientIp={}", shareCode, clientIp, e);
            return false;
        }
    }

    @Override
    @Transactional
    public boolean recordDownload(String shareCode, String clientIp) {
        if (!StringUtils.hasText(shareCode)) {
            return false;
        }

        try {
            ShareLink shareLink = baseMapper.selectByShareCode(shareCode);
            if (shareLink == null) {
                return false;
            }

            // 检查下载次数限制
            if (shareLink.getMaxDownloadCount() != null && 
                shareLink.getCurrentDownloadCount() >= shareLink.getMaxDownloadCount()) {
                throw new IllegalStateException("下载次数已达上限");
            }

            // 更新下载统计
            int affectedRows = baseMapper.updateDownloadStats(shareLink.getId(), LocalDateTime.now(), clientIp);
            
            log.info("记录分享下载成功: shareCode={}, clientIp={}, affectedRows={}", shareCode, clientIp, affectedRows);
            return affectedRows > 0;

        } catch (Exception e) {
            log.error("记录分享下载失败: shareCode={}, clientIp={}", shareCode, clientIp, e);
            return false;
        }
    }

    @Override
    public List<ShareLinkVO> getShareLinksByFileId(String fileId) {
        if (!StringUtils.hasText(fileId)) {
            throw new IllegalArgumentException("文件ID不能为空");
        }

        List<ShareLink> shareLinks = baseMapper.selectByFileId(fileId);
        return convertToVOList(shareLinks);
    }

    @Override
    public List<ShareLinkVO> getValidShareLinks(String fileId) {
        if (!StringUtils.hasText(fileId)) {
            throw new IllegalArgumentException("文件ID不能为空");
        }

        List<ShareLink> shareLinks = baseMapper.selectValidByFileId(fileId, LocalDateTime.now());
        return convertToVOList(shareLinks);
    }

    @Override
    public Page<ShareLinkVO> getUserShareLinks(String creatorId, Page<ShareLink> page) {
        if (!StringUtils.hasText(creatorId)) {
            throw new IllegalArgumentException("创建者ID不能为空");
        }

        // TODO: 使用MyBatis-Plus分页查询
        List<ShareLink> shareLinks = baseMapper.selectByCreatorId(creatorId, null);
        List<ShareLinkVO> voList = convertToVOList(shareLinks);
        
        Page<ShareLinkVO> result = new Page<>(page.getCurrent(), page.getSize());
        result.setRecords(voList);
        result.setTotal(voList.size());
        
        return result;
    }

    @Override
    @Transactional
    public boolean updateShareLink(String shareId, Integer shareType, String password,
                                   LocalDateTime validUntil, Integer maxAccessCount, 
                                   Integer maxDownloadCount, String description) {
        if (!StringUtils.hasText(shareId)) {
            throw new IllegalArgumentException("分享ID不能为空");
        }

        try {
            ShareLink shareLink = getById(shareId);
            if (shareLink == null) {
                return false;
            }

            // 更新字段
            if (shareType != null) shareLink.setShareType(shareType);
            if (password != null) {
                if (!StringUtils.hasText(password)) {
                    shareLink.setSharePassword(null);
                    shareLink.setExtraAttributes(null);
                } else {
                    String hashed = BCrypt.hashpw(password, BCrypt.gensalt());
                    shareLink.setSharePassword(hashed);
                    shareLink.setExtraAttributes(null);
                }
            }
            if (validUntil != null) shareLink.setValidUntil(validUntil);
            if (maxAccessCount != null) shareLink.setMaxAccessCount(maxAccessCount);
            if (maxDownloadCount != null) shareLink.setMaxDownloadCount(maxDownloadCount);
            if (description != null) shareLink.setDescription(description);
            shareLink.setUpdateTime(LocalDateTime.now());

            boolean success = updateById(shareLink);
            
            log.info("更新分享链接成功: shareId={}, success={}", shareId, success);
            return success;

        } catch (Exception e) {
            log.error("更新分享链接失败: shareId={}", shareId, e);
            return false;
        }
    }

    @Override
    @Transactional
    public boolean disableShareLink(String shareId) {
        return updateShareStatus(shareId, 0);
    }

    @Override
    @Transactional
    public boolean enableShareLink(String shareId) {
        return updateShareStatus(shareId, 1);
    }

    private boolean updateShareStatus(String shareId, Integer status) {
        if (!StringUtils.hasText(shareId)) {
            return false;
        }

        try {
            ShareLink shareLink = getById(shareId);
            if (shareLink == null) {
                return false;
            }

            shareLink.setStatus(status);
            shareLink.setUpdateTime(LocalDateTime.now());
            
            boolean success = updateById(shareLink);
            log.info("更新分享链接状态成功: shareId={}, status={}, success={}", shareId, status, success);
            return success;

        } catch (Exception e) {
            log.error("更新分享链接状态失败: shareId={}, status={}", shareId, status, e);
            return false;
        }
    }

    @Override
    @Transactional
    public boolean deleteShareLink(String shareId) {
        if (!StringUtils.hasText(shareId)) {
            return false;
        }

        try {
            boolean success = removeById(shareId);
            log.info("删除分享链接成功: shareId={}, success={}", shareId, success);
            return success;

        } catch (Exception e) {
            log.error("删除分享链接失败: shareId={}", shareId, e);
            return false;
        }
    }

    @Override
    @Transactional
    public boolean deleteAllShareLinks(String fileId) {
        if (!StringUtils.hasText(fileId)) {
            return false;
        }

        try {
            int affectedRows = baseMapper.deleteByFileId(fileId);
            log.info("删除文件所有分享链接成功: fileId={}, deletedCount={}", fileId, affectedRows);
            return affectedRows > 0;

        } catch (Exception e) {
            log.error("删除文件所有分享链接失败: fileId={}", fileId, e);
            return false;
        }
    }

    @Override
    @Transactional
    public int processExpiredLinks() {
        try {
            List<ShareLink> expiredLinks = baseMapper.selectExpiredLinks(LocalDateTime.now(), 1000);
            if (expiredLinks.isEmpty()) {
                return 0;
            }

            List<String> shareIds = expiredLinks.stream()
                    .map(ShareLink::getId)
                    .collect(Collectors.toList());

            int affectedRows = baseMapper.batchUpdateStatus(shareIds, 0); // 设为禁用状态
            
            log.info("处理过期分享链接完成: processedCount={}", affectedRows);
            return affectedRows;

        } catch (Exception e) {
            log.error("处理过期分享链接失败", e);
            return 0;
        }
    }

    @Override
    @Transactional
    public int processAccessLimitReachedLinks() {
        try {
            List<ShareLink> limitReachedLinks = baseMapper.selectAccessLimitReachedLinks(1000);
            if (limitReachedLinks.isEmpty()) {
                return 0;
            }

            List<String> shareIds = limitReachedLinks.stream()
                    .map(ShareLink::getId)
                    .collect(Collectors.toList());

            int affectedRows = baseMapper.batchUpdateStatus(shareIds, 0); // 设为禁用状态
            
            log.info("处理访问次数达限分享链接完成: processedCount={}", affectedRows);
            return affectedRows;

        } catch (Exception e) {
            log.error("处理访问次数达限分享链接失败", e);
            return 0;
        }
    }

    @Override
    public String generateShareCode() {
        // 生成8位随机字符串
        String randomStr = RandomUtil.randomString(8);
        // 使用时间戳增加唯一性
        String timestamp = String.valueOf(System.currentTimeMillis());
        // MD5哈希后取前8位
        String hash = DigestUtil.md5Hex(randomStr + timestamp);
        return hash.substring(0, 8).toUpperCase();
    }

    @Override
    public String generateShareUrl(String shareCode) {
        if (!StringUtils.hasText(shareCode)) {
            return null;
        }
        String base = storageProperties.getShare() != null ? storageProperties.getShare().getBaseUrl() : null;
        String prefix = storageProperties.getShare() != null ? storageProperties.getShare().getPathPrefix() : "/share/";
        if (!StringUtils.hasText(base)) {
            return prefix + shareCode;
        }
        // 规范化拼接
        String p = prefix.startsWith("/") ? prefix : "/" + prefix;
        if (base.endsWith("/")) {
            base = base.substring(0, base.length() - 1);
        }
        return base + p + shareCode;
    }

    // ==================== 密码哈希工具 ====================
    private String generateSalt() {
        byte[] bytes = new byte[16];
        new SecureRandom().nextBytes(bytes);
        return bytesToHex(bytes);
    }

    private String hashPasswordWithSalt(String password, String salt) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] input = (salt + ":" + password).getBytes(java.nio.charset.StandardCharsets.UTF_8);
            byte[] hashed = digest.digest(input);
            return bytesToHex(hashed);
        } catch (Exception e) {
            throw new RuntimeException("密码哈希失败", e);
        }
    }

    private boolean verifyPassword(String rawPassword, String salt, String expectedHashHex) {
        String calc = hashPasswordWithSalt(rawPassword, salt);
        return calc.equalsIgnoreCase(expectedHashHex);
    }

    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    private String extractSalt(String extraAttributes) {
        if (!StringUtils.hasText(extraAttributes)) return null;
        try {
            JSONObject obj = JSONUtil.parseObj(extraAttributes);
            return obj.getStr("salt");
        } catch (Exception e) {
            return null;
        }
    }

    private JSONObject safeParseJson(String json) {
        if (!StringUtils.hasText(json)) return null;
        try {
            return JSONUtil.parseObj(json);
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public Map<String, Object> getShareStats(String shareId) {
        if (!StringUtils.hasText(shareId)) {
            return new HashMap<>();
        }

        ShareLink shareLink = getById(shareId);
        if (shareLink == null) {
            return new HashMap<>();
        }

        Map<String, Object> stats = new HashMap<>();
        stats.put("shareId", shareId);
        stats.put("accessCount", shareLink.getCurrentAccessCount());
        stats.put("downloadCount", shareLink.getCurrentDownloadCount());
        stats.put("maxAccessCount", shareLink.getMaxAccessCount());
        stats.put("maxDownloadCount", shareLink.getMaxDownloadCount());
        stats.put("lastAccessTime", shareLink.getLastAccessTime());
        // 下载时间未在表结构中单独记录
        stats.put("createTime", shareLink.getCreateTime());
        
        return stats;
    }

    @Override
    public Map<String, Object> getUserShareStats(String creatorId, Integer days) {
        if (!StringUtils.hasText(creatorId)) {
            return new HashMap<>();
        }

        LocalDateTime startTime = days != null ? LocalDateTime.now().minusDays(days) : null;
        LocalDateTime endTime = LocalDateTime.now();
        
        int shareCount = baseMapper.countByCreatorIdAndTimeRange(creatorId, startTime, endTime);
        
        Map<String, Object> stats = new HashMap<>();
        stats.put("creatorId", creatorId);
        stats.put("shareCount", shareCount);
        stats.put("days", days);
        stats.put("startTime", startTime);
        stats.put("endTime", endTime);
        
        return stats;
    }

    @Override
    public List<ShareLinkVO> getPopularShareLinks(Integer limit, Integer days) {
        List<ShareLink> popularLinks = baseMapper.selectPopularLinks(limit, days);
        return convertToVOList(popularLinks);
    }

    @Override
    public String getShareTypeText(Integer shareType) {
        if (shareType == null) {
            return "未知";
        }
        switch (shareType) {
            case 1: return "公开分享";
            case 2: return "密码分享";
            case 3: return "私有分享";
            default: return "未知类型";
        }
    }

    @Override
    public String getShareStatusText(Integer status) {
        if (status == null) {
            return "未知";
        }
        switch (status) {
            case 1: return "正常";
            case 0: return "禁用";
            case -1: return "已删除";
            default: return "未知状态";
        }
    }

    @Override
    public ShareLinkVO convertToVO(ShareLink shareLink) {
        if (shareLink == null) {
            return null;
        }

        ShareLinkVO vo = new ShareLinkVO();
        vo.setId(shareLink.getId());
        vo.setFileId(shareLink.getFileId());
        vo.setShareCode(shareLink.getShareCode());
        vo.setShareUrl(generateShareUrl(shareLink.getShareCode()));
        vo.setShareType(shareLink.getShareType());
        vo.setShareTypeText(getShareTypeText(shareLink.getShareType()));
        vo.setStatus(shareLink.getStatus());
        vo.setStatusText(getShareStatusText(shareLink.getStatus()));
        vo.setAccessPermission(shareLink.getAccessPermission());
        vo.setMaxAccessCount(shareLink.getMaxAccessCount());
        vo.setCurrentAccessCount(shareLink.getCurrentAccessCount());
        vo.setMaxDownloadCount(shareLink.getMaxDownloadCount());
        vo.setCurrentDownloadCount(shareLink.getCurrentDownloadCount());
        vo.setValidFrom(shareLink.getValidFrom());
        vo.setValidUntil(shareLink.getValidUntil());
        vo.setCreatorId(shareLink.getCreatorId());
        vo.setLastAccessTime(shareLink.getLastAccessTime());
        vo.setLastAccessIp(shareLink.getLastAccessIp());
        vo.setDescription(shareLink.getDescription());
        vo.setCreateTime(shareLink.getCreateTime());
        vo.setUpdateTime(shareLink.getUpdateTime());

        // 计算状态
        LocalDateTime now = LocalDateTime.now();
        vo.setValid(shareLink.getStatus() == 1);
        vo.setExpired(shareLink.getValidUntil() != null && now.isAfter(shareLink.getValidUntil()));
        vo.setRequiresPassword(shareLink.getShareType() == 2 && StringUtils.hasText(shareLink.getSharePassword()));
        vo.setOneTimeLink(shareLink.getMaxAccessCount() != null && shareLink.getMaxAccessCount() == 1);
        vo.setCanDownload(true); // TODO: 根据权限设置
        vo.setCanPreview(true); // TODO: 根据权限设置
        vo.setCanAccess(shareLink.getMaxAccessCount() == null || 
                       shareLink.getCurrentAccessCount() < shareLink.getMaxAccessCount());
        vo.setCanDownloadMore(shareLink.getMaxDownloadCount() == null || 
                             shareLink.getCurrentDownloadCount() < shareLink.getMaxDownloadCount());

        // TODO: 设置创建者名称、文件名称等扩展字段
        // vo.setCreatorName(getUserName(shareLink.getCreateBy()));
        // vo.setFileName(getFileName(shareLink.getFileId()));

        return vo;
    }

    @Override
    public List<ShareLinkVO> convertToVOList(List<ShareLink> shareLinks) {
        if (shareLinks == null || shareLinks.isEmpty()) {
            return List.of();
        }
        return shareLinks.stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
    }
}
